package org.xiaoyu.feign.core;

import java.util.ArrayList;

import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.StringUtils;

import feign.Client;
import feign.Contract;
import feign.Feign;
import feign.InvocationHandlerFactory;
import feign.Logger;
import feign.Logger.NoOpLogger;
import feign.QueryMapEncoder;
import feign.Request;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
import feign.codec.ErrorDecoder;

public class FeignFactoryBean<T> implements FactoryBean<T> , InitializingBean, ApplicationContextAware {
	
	private org.slf4j.Logger log = LoggerFactory.getLogger(FeignFactoryBean.class);
		
	private Class<T> mapperInterface;
	
	private String[] urls;

	private String name;

	private Decoder decoder = new Decoder.Default();
	
    private Encoder encoder = new Encoder.Default();
	
	private ErrorDecoder errorDecoder = new ErrorDecoder.Default();

    private Client client = new Client.Default(null, null);
    
    private Contract contract = new Contract.Default();
    
    private QueryMapEncoder queryMapEncoder = new QueryMapEncoder.Default();
    
    private Retryer retryer = new Retryer.Default();
    
    private Logger logger = new NoOpLogger();
    
	private Boolean decode404 = false;
    
	private InvocationHandlerFactory invocationHandlerFactory =
            new InvocationHandlerFactory.Default();
	
	private Request.Options options = new Request.Options();
	
	private ApplicationContext applicationContext;

	public FeignFactoryBean() {
		
	}
	
	public FeignFactoryBean(Class<T> mapperInterface, String[] urls) {
		this.mapperInterface = mapperInterface;
		this.urls = urls;
	}

	@Override
	public T getObject() throws Exception {
		Feign.Builder builder = Feign.builder();
		if(decode404) {
			builder.decode404();
		}
		builder.options(options)
		.invocationHandlerFactory(invocationHandlerFactory)
		.retryer(retryer)
		.logger(logger)
		.queryMapEncoder(queryMapEncoder)
		.contract(contract)
		.decoder(decoder)
		.encoder(encoder)				
		.client(client)
		.errorDecoder(errorDecoder);
		if(urls.length > 0) {
			if(urls.length == 1) {
				log.info("[{}] using specified service address!!", urls[0]);
				return builder.target(mapperInterface, checkUrl(urls[0]));
			}else {
				ArrayList<String> urlsList = new ArrayList<>();
				StringBuilder strUrls = new StringBuilder();
				for (String u : urls) {
					urlsList.add(checkUrl(u));
					strUrls.append(u + ",");
				}
				log.info("[{}] using specified service address!!", strUrls);
				CustomTarget<T> customTarget = new CustomTarget<T>(mapperInterface, urlsList);
				return builder.target(customTarget);
			}
		}else if(!StringUtils.isEmpty(name)) {
			log.info("[{}] using online service address!!", name);
			builder.client(new FeignOkHttp3Client(false));
			return builder.target(mapperInterface, checkUrl(name));
		}
		throw new IllegalArgumentException();
	}
	
	
	private String checkUrl(String url) {
		if(!url.startsWith("http")) {
			url = "http://" + url;
		}
		return url;
	}

	@Override
	public Class<?> getObjectType() {
		return mapperInterface;
	}
    
	
	public Request.Options getOptions() {
		return options;
	}

	public void setOptions(Request.Options options) {
		this.options = options;
	}
	
    public Boolean getDecode404() {
		return decode404;
	}

	public void setDecode404(Boolean decode404) {
		this.decode404 = decode404;
	}
	
	public InvocationHandlerFactory getInvocationHandlerFactory() {
		return invocationHandlerFactory;
	}

	public void setInvocationHandlerFactory(InvocationHandlerFactory invocationHandlerFactory) {
		this.invocationHandlerFactory = invocationHandlerFactory;
	}
    
	public Decoder getDecoder() {
		return decoder;
	}

	public void setDecoder(Decoder decoder) {
		this.decoder = decoder;
	}

	public ErrorDecoder getErrorDecoder() {
		return errorDecoder;
	}

	public void setErrorDecoder(ErrorDecoder errorDecoder) {
		this.errorDecoder = errorDecoder;
	}

	public Encoder getEncoder() {
		return encoder;
	}

	public void setEncoder(Encoder encoder) {
		this.encoder = encoder;
	}

	public Client getClient() {
		return client;
	}

	public void setClient(Client client) {
		this.client = client;
	}

	public Contract getContract() {
		return contract;
	}

	public void setContract(Contract contract) {
		this.contract = contract;
	}

	public QueryMapEncoder getQueryMapEncoder() {
		return queryMapEncoder;
	}

	public void setQueryMapEncoder(QueryMapEncoder queryMapEncoder) {
		this.queryMapEncoder = queryMapEncoder;
	}

	public Retryer getRetryer() {
		return retryer;
	}

	public void setRetryer(Retryer retryer) {
		this.retryer = retryer;
	}

	public Logger getLogger() {
		return logger;
	}

	public void setLogger(Logger logger) {
		this.logger = logger;
	}

	public Class<T> getMapperInterface() {
		return mapperInterface;
	}

	public void setMapperInterface(Class<T> mapperInterface) {
		this.mapperInterface = mapperInterface;
	}
	
	public String[] getUrls() {
		return urls;
	}

	public void setUrls(String[] urls) {
		this.urls = urls;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		// TODO Auto-generated method stub
		
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}
